package com.tjvib.view.activity;

import static com.tjvib.Constants.PARAM_ACCX;
import static com.tjvib.Constants.PARAM_ACCY;
import static com.tjvib.Constants.PARAM_ACCZ;
import static com.tjvib.Constants.PARAM_ALL;

import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.TextView;

import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.LegendRenderer;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.DataPointInterface;
import com.jjoe64.graphview.series.LineGraphSeries;
import com.jjoe64.graphview.series.OnDataPointTapListener;
import com.jjoe64.graphview.series.PointsGraphSeries;
import com.jjoe64.graphview.series.Series;
import com.tjvib.Constants;
import com.tjvib.R;
import com.tjvib.base.BaseActivity;
import com.tjvib.util.LogUtil;
import com.tjvib.util.Manager.DataSetManager;
import com.tjvib.math.Complex;
import com.tjvib.math.FFT;
import com.tjvib.presenter.BasePresenter;
import com.tjvib.sensor.SensorData;
import com.tjvib.util.Manager.AppManager;
import com.tjvib.util.Manager.UserManager;
import com.tjvib.util.PoiUtil;
import com.tjvib.util.PressUtil;
import com.tjvib.util.ToastUtil;
import com.tjvib.view.dialog.LoadingDialog;
import com.tjvib.widget.TitleLayout;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DampingActivity extends BaseActivity {

    private GraphView damp_gv_data;
    private Spinner damp_sp_sensor;
    private RadioButton damp_rb_all, damp_rb_accx, damp_rb_accy, damp_rb_accz;
    private TextView damp_tv_x, damp_tv_y, damp_tv_choose, damp_tv_zn;
    private Button damp_btn_export;
    private TitleLayout damp_tit;

    private String sensorSelect = "";
    private LineGraphSeries<DataPoint> mSeries1 = new LineGraphSeries<>(),
            mSeries2 = new LineGraphSeries<>(),
            mSeries3 = new LineGraphSeries<>(),
            halfSeries = new LineGraphSeries<>();
    
    private PointsGraphSeries<DataPoint> pSeries = new PointsGraphSeries<>();
    private DataPoint[] dataPointsX, dataPointsY, dataPointsZ;
    private DataPoint peakDataPoint;
    private static int curSelectedParm = PARAM_ALL;
    

    private OnDataPointTapListener onDataPointTapListenerForDamping = new OnDataPointTapListener() {
        @Override
        public void onTap(Series series, DataPointInterface dataPoint) {
            pSeries.resetData(new DataPoint[]{
                    new DataPoint(dataPoint.getX(), dataPoint.getY())
            });

            double halfY = dataPoint.getY() / Math.sqrt(2);
            halfSeries.resetData(new DataPoint[]{
                    new DataPoint(0, halfY),
                    new DataPoint(50D, halfY)
            });
            switch (curSelectedParm){
                case PARAM_ACCX:
                    damp_tv_y.setText("AccX=" + dataPoint.getY());
                    findHalfPoint(1);
                    break;
                case PARAM_ACCY:
                    damp_tv_y.setText("AccY=" + dataPoint.getY());
                    findHalfPoint(2);
                    break;
                case PARAM_ACCZ:
                    damp_tv_y.setText("AccZ=" + dataPoint.getY());
                    findHalfPoint(3);
                    break;
                case PARAM_ALL:
                    double x = 0, y = 0, z = 0;
                    for(int i = 0; i < dataPointsX.length; i++){
                        if(dataPointsX[i].getX() == dataPoint.getX()){
                            x = dataPointsX[i].getY();
                            break;
                        }
                    }
                    for(int i = 0; i < dataPointsY.length; i++){
                        if(dataPointsY[i].getX() == dataPoint.getX()){
                            y = dataPointsY[i].getY();
                            break;
                        }
                    }
                    for(int i = 0; i < dataPointsZ.length; i++){
                        if(dataPointsZ[i].getX() == dataPoint.getX()){
                            z = dataPointsZ[i].getY();
                            break;
                        }
                    }
                    damp_tv_y.setText("AccX=" + x + "\nAccY=" + y +"\nAccZ=" + z);
                    break;
            }

            damp_tv_x.setText("" + dataPoint.getX());
            
            peakDataPoint = new DataPoint(dataPoint.getX(), dataPoint.getY());
            damp_tv_choose.setText("" + dataPoint.getX() + "Hz");
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    protected int initLayout() {
        return R.layout.activity_damping;
    }

    @Override
    protected boolean initView() {
        damp_tit = findViewById(R.id.damp_tit);
        damp_tit.setTitle(getIntent().getStringExtra("METHOD"));

        damp_btn_export = findViewById(R.id.damp_btn_export);
        damp_btn_export.setOnClickListener(nv-> {
                FileReadWriteTask fileReadWriteTask = new FileReadWriteTask();
                fileReadWriteTask.execute();
        });

        damp_gv_data = findViewById(R.id.damp_gv_data);

        damp_sp_sensor = findViewById(R.id.damp_sp_sensor);

        damp_rb_all = findViewById(R.id.damp_rb_all);
        damp_rb_accx = findViewById(R.id.damp_rb_accx);
        damp_rb_accy = findViewById(R.id.damp_rb_accy);
        damp_rb_accz = findViewById(R.id.damp_rb_accz);
        damp_rb_all.setOnCheckedChangeListener((compoundButton, b) -> {
            if (b) onAxisChange(PARAM_ALL);
        });
        damp_rb_accx.setOnCheckedChangeListener((compoundButton, b) -> {
            if (b) onAxisChange(PARAM_ACCX);
        });
        damp_rb_accy.setOnCheckedChangeListener((compoundButton, b) -> {
            if (b) onAxisChange(PARAM_ACCY);
        });
        damp_rb_accz.setOnCheckedChangeListener((compoundButton, b) -> {
            if (b) onAxisChange(PARAM_ACCZ);
        });

        damp_tv_x = findViewById(R.id.damp_tv_x);
        damp_tv_y = findViewById(R.id.damp_tv_y);
        damp_tv_choose = findViewById(R.id.damp_tv_choose);
        damp_tv_zn = findViewById(R.id.damp_tv_zn);

        return true;
    }

    @Override
    protected boolean initData() {
        List<String> sensorList = DataSetManager.getInstance().getSensorList();
        SpinnerAdapter spinnerAdapter = new ArrayAdapter<>(this, R.layout.item_spinner_dropdown, sensorList);
        damp_sp_sensor.setAdapter(spinnerAdapter);
        damp_sp_sensor.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
                sensorSelect = sensorList.get(position);
                drawGraph();
                damp_rb_all.setChecked(true);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parent) {
            }
        });

        return true;
    }

    @Override
    protected BasePresenter genPresenter() {
        return null;
    }

    private void drawGraph() {

        runOnUiThread(() -> {
            mSeries1 = new LineGraphSeries<>();
            mSeries2 = new LineGraphSeries<>();
            mSeries3 = new LineGraphSeries<>();
            pSeries = new PointsGraphSeries<>();
            halfSeries = new LineGraphSeries<>();
            pSeries.setShape(PointsGraphSeries.Shape.POINT);
            damp_gv_data.getViewport().setMinX(0);
            damp_gv_data.getViewport().setScalable(true);
            damp_gv_data.getViewport().setScrollable(true);
            mSeries1.setThickness(3);
            mSeries2.setThickness(3);
            mSeries3.setThickness(3);
            halfSeries.setThickness(3);
            pSeries.setSize(10);
            pSeries.setColor(Color.BLACK);
            this.mSeries1.setColor(Color.BLUE);
            this.mSeries2.setColor(Color.GREEN);
            this.mSeries3.setColor(Color.RED);
            this.damp_gv_data.setBackgroundColor(Color.WHITE);
            halfSeries.setColor(Color.CYAN);
            mSeries1.setTitle("AccX");
            mSeries2.setTitle("AccY");
            mSeries3.setTitle("AccZ");
            halfSeries.setTitle("Clicked/sqrt(2)");
            pSeries.setTitle("Clicked");
            mSeries1.setOnDataPointTapListener(onDataPointTapListenerForDamping);
            mSeries2.setOnDataPointTapListener(onDataPointTapListenerForDamping);
            mSeries3.setOnDataPointTapListener(onDataPointTapListenerForDamping);
            damp_gv_data.getLegendRenderer().setBackgroundColor(Color.TRANSPARENT);
            damp_gv_data.getLegendRenderer().setVisible(true);
            damp_gv_data.getLegendRenderer().setTextSize(18);
            damp_gv_data.getLegendRenderer().setTextColor(R.color.main_grey_2);
            damp_gv_data.getLegendRenderer().setAlign(LegendRenderer.LegendAlign.TOP);
            damp_gv_data.getGridLabelRenderer().setHorizontalAxisTitle("频率(Hz)");
            damp_gv_data.getGridLabelRenderer().setVerticalAxisTitle("幅值(g)");
            damp_gv_data.getGridLabelRenderer().setHorizontalAxisTitleTextSize(24);
            damp_gv_data.getGridLabelRenderer().setVerticalAxisTitleTextSize(24);
            damp_gv_data.addSeries(mSeries1);
            damp_gv_data.addSeries(mSeries2);
            damp_gv_data.addSeries(mSeries3);
            damp_gv_data.addSeries(pSeries);
            damp_gv_data.addSeries(halfSeries);
            halfSeries.resetData(new DataPoint[]{
                    new DataPoint(0, 0),
                    new DataPoint(50D, 0)
            });

            initGraphViewForDamping();
        });
    }

    private void initGraphViewForDamping() {
        runOnUiThread(() -> {
            mSeries1.resetData(new DataPoint[]{});
            mSeries2.resetData(new DataPoint[]{});
            mSeries3.resetData(new DataPoint[]{});
            pSeries.resetData(new DataPoint[]{});

            double len, f0;
            int cutSize;
            List<? extends SensorData> dataList;
            if (sensorSelect.equals("local")) {
                dataList = DataSetManager.getInstance().getDataSetContent(-1);
            } else {
                dataList = DataSetManager.getInstance().getDataSetContent(Integer.parseInt(sensorSelect));
            }

            cutSize = dataList.size();
            while ((cutSize & (cutSize - 1)) != 0) {
                cutSize--;
            }

//            if (cutSize < 50) {
//                damp_gv_data.getViewport().setMinX(0d);
//                damp_gv_data.getViewport().setMaxX(cutSize);
//            }

            double startTime = dataList.get(0).timestamp;
            double endTime = dataList.get(cutSize - 1).timestamp;
            double dt = (endTime - startTime) / (cutSize - 1);
            double fs = 1.0 / dt;

            Complex[] complex_accx0 = new Complex[cutSize];
            Complex[] complex_accy0 = new Complex[cutSize];
            Complex[] complex_accz0 = new Complex[cutSize];

            for (int i = 0; i < cutSize; i++) {
                complex_accx0[i] = new Complex(dataList.get(i).acc[0], 0D);
                complex_accy0[i] = new Complex(dataList.get(i).acc[1], 0D);
                complex_accz0[i] = new Complex(dataList.get(i).acc[2], 0D);
            }

            double N = cutSize;
            //double fs = 100;//采样频率
            //double dt = 1/fs;//采样间隔
            double time = N / fs;
            f0 = 1 / (dt * N);
            len = Math.ceil((N - 1) / 2);
            damp_gv_data.getViewport().setMaxX(len*f0);

            Complex[] complex_accx = FFT.fft(complex_accx0);
            Complex[] complex_accy = FFT.fft(complex_accy0);
            Complex[] complex_accz = FFT.fft(complex_accz0);

            dataPointsX = new DataPoint[(int) len + 1];
            dataPointsY = new DataPoint[(int) len + 1];
            dataPointsZ = new DataPoint[(int) len + 1];

            double dpxh, dpyh, dpzh;
            for (int i = 0; i <= len; i++) {
                dpxh = Math.sqrt(complex_accx[i].re() * complex_accx[i].re() + complex_accx[i].im() * complex_accx[i].im());
                dataPointsX[i] = new DataPoint(i * f0, 2 * dpxh * dpxh * time);

                dpyh = Math.sqrt(complex_accy[i].re() * complex_accy[i].re() + complex_accy[i].im() * complex_accy[i].im());
                dataPointsY[i] = new DataPoint(i * f0, 2 * dpyh * dpyh * time);

                dpzh = Math.sqrt(complex_accz[i].re() * complex_accz[i].re() + complex_accz[i].im() * complex_accz[i].im());
                dataPointsZ[i] = new DataPoint(i * f0, 2 * dpzh * dpzh * time);

                mSeries1.appendData(dataPointsX[i], true, 1000);
                mSeries2.appendData(dataPointsY[i], true, 1000);
                mSeries3.appendData(dataPointsZ[i], true, 1000);
            }

            pSeries.resetData(new DataPoint[]{
                    new DataPoint(dataPointsX[0].getX(), dataPointsX[0].getY())
            });
        });
    }


    private void findHalfPoint(int axis) {
        int i = 0;
        int mid;
        double halfY = 0, halfX1 = 0, halfX2 = 0;
        switch (axis) {
            case 1:
                while (dataPointsX[i].getX() < peakDataPoint.getX()) {
                    i++;
                }
                mid = i;
                while (i < dataPointsX.length && dataPointsX[i].getY() > halfY) {
                    i++;
                }
                if (i == dataPointsX.length) {
                    i--;
                }
                halfX2 = (halfY - dataPointsX[i - 1].getY()) * (dataPointsX[i].getX() - dataPointsX[i - 1].getX())
                        / (dataPointsX[i].getY() - dataPointsX[i - 1].getY()) + dataPointsX[i - 1].getX();

                i = mid;
                while (i > 0 && dataPointsX[i].getY() > halfY) {
                    i--;
                }
                halfX1 = (halfY - dataPointsX[i].getY()) * (dataPointsX[i + 1].getX() - dataPointsX[i].getX())
                        / (dataPointsX[i + 1].getY() - dataPointsX[i].getY()) + dataPointsX[i].getX();
                break;
            case 2:
                while (dataPointsY[i].getX() < peakDataPoint.getX()) {
                    i++;
                }
                mid = i;
                while (i < dataPointsY.length && dataPointsY[i].getY() > halfY) {
                    i++;
                }
                if (i == dataPointsY.length) {
                    i--;
                }
                halfX2 = (halfY - dataPointsY[i - 1].getY()) * (dataPointsY[i].getX() - dataPointsY[i - 1].getX())
                        / (dataPointsY[i].getY() - dataPointsY[i - 1].getY()) + dataPointsY[i - 1].getX();

                i = mid;
                while (i > 0 && dataPointsY[i].getY() > halfY) {
                    i--;
                }
                halfX1 = (halfY - dataPointsY[i].getY()) * (dataPointsY[i + 1].getX() - dataPointsY[i].getX())
                        / (dataPointsY[i + 1].getY() - dataPointsY[i].getY()) + dataPointsY[i].getX();
                break;
            case 3:
                while (dataPointsZ[i].getX() < peakDataPoint.getX()) {
                    i++;
                }
                mid = i;
                while (i < dataPointsZ.length && dataPointsZ[i].getY() > halfY) {
                    i++;
                }
                if (i == dataPointsZ.length) {
                    i--;
                }
                halfX2 = (halfY - dataPointsZ[i - 1].getY()) * (dataPointsZ[i].getX() - dataPointsZ[i - 1].getX())
                        / (dataPointsZ[i].getY() - dataPointsZ[i - 1].getY()) + dataPointsZ[i - 1].getX();

                i = mid;
                while (i > 0 && dataPointsZ[i].getY() > halfY) {
                    i--;
                }
                halfX1 = (halfY - dataPointsZ[i].getY()) * (dataPointsZ[i + 1].getX() - dataPointsZ[i].getX())
                        / (dataPointsZ[i + 1].getY() - dataPointsZ[i].getY()) + dataPointsZ[i].getX();
                break;
        }

        damp_tv_zn.setText("" + (halfX2 - halfX1) / (2 * peakDataPoint.getX()));
    }


    private void onAxisChange(int axis) {
        curSelectedParm = axis;
        damp_gv_data.removeAllSeries();
        damp_gv_data.addSeries(pSeries);
        damp_gv_data.addSeries(halfSeries);
        switch (axis) {
            case PARAM_ACCX:
                damp_gv_data.addSeries(mSeries1);
                mSeries1.resetData(dataPointsX);
                break;
            case PARAM_ACCY:
                damp_gv_data.addSeries(mSeries2);
                mSeries2.resetData(dataPointsY);
                break;
            case PARAM_ACCZ:
                damp_gv_data.addSeries(mSeries3);
                mSeries3.resetData(dataPointsZ);
                break;
            case PARAM_ALL:
                damp_gv_data.addSeries(mSeries1);
                mSeries1.resetData(dataPointsX);
                damp_gv_data.addSeries(mSeries2);
                mSeries2.resetData(dataPointsY);
                damp_gv_data.addSeries(mSeries3);
                mSeries3.resetData(dataPointsZ);
        }
    }


    private class FileReadWriteTask extends AsyncTask<Void, Void, Void> {
        private LoadingDialog loadingDialog = null;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            loadingDialog = new LoadingDialog(DampingActivity.this);
            loadingDialog.showDialog(DampingActivity.this, "正在输出报告...", 5);
        }

        @Override
        protected Void doInBackground(Void... voids) {
            SimpleDateFormat sdf = new SimpleDateFormat();
            sdf.applyPattern("yyyy-MM-dd HH:mm:ss");
            Date date = new Date();

            Map<String, String> replacements = new HashMap<>();
            replacements.put("【时间】", sdf.format(date));
            replacements.put("【姓名】", UserManager.getInstance().getUsername());
            replacements.put("【传感器】", damp_sp_sensor.getSelectedItem().toString().trim());
            replacements.put("【频率】", damp_tv_x.getText().toString().trim());
            replacements.put("【幅值】", damp_tv_y.getText().toString().trim());
            replacements.put("【固有频率】", damp_tv_choose.getText().toString().trim());
            replacements.put("【阻尼比】", damp_tv_zn.getText().toString().trim());
            Bitmap bitmap = PoiUtil.loadBitmapFromView(damp_gv_data);

            synchronized (PoiUtil.class) {
                PoiUtil.replaceFieldsWithImage(bitmap, DampingActivity.this, Constants.METHOD_DAMPING,
                        sdf.format(date) + ".docx", replacements);
            }
            return null;
        }

        @Override
        protected void onPostExecute(Void unused) {
            super.onPostExecute(unused);
            if(loadingDialog != null) loadingDialog.dismiss();
            ToastUtil.show("报告已保存");
        }
    }
}